#pragma once

#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <sstream>
#include <memory>
#include <sys/stat.h>
#include <experimental/filesystem>
#include <jsoncpp/json/json.h>

#include "bundle.h"

namespace cloud
{
    namespace fs = std::experimental::filesystem;
    class FileUtil
    {
    public:
        FileUtil(const std::string &filename) : _filename(filename) {}

        bool Remove()
        {
            if(Exists() == false)
            {
                return false;
            }
            remove(_filename.c_str());
            return true;
        }
        int64_t FileSize()
        {
            struct stat st;

            if (stat(_filename.c_str(), &st) < 0)
            {
                std::cerr << "Get FileSize Error" << std::endl;
                return -1;
            }
            
            return st.st_size;
        }

        time_t LastMTime()
        {
            struct stat st;

            if (stat(_filename.c_str(), &st) < 0)
            {
                std::cerr << "Get LastMTime Error" << std::endl;
                return -1;
            }

            return st.st_mtime;
        }

        time_t LastATime()
        {
            struct stat st;

            if (stat(_filename.c_str(), &st) < 0)
            {
                std::cerr << "Get LastATIme Error" << std::endl;
                return -1;
            }

            return st.st_atime;
        }

        std::string FileName()
        {
            // ./abc/test/a.html

            size_t pos = _filename.find_last_of("/");
            if (pos == std::string::npos)
            {
                return _filename;
            }

            // 找到
            return _filename.substr(pos + 1);
        }

        bool GetPosLen(std::string *body, size_t pos, size_t len)
        {
            std::ifstream ifs;
            ifs.open(_filename, std::ios::binary);

            if (ifs.is_open() == false)
            {
                std::cerr << "ifs open Error" << std::endl;
                return false;
            }

            size_t fsize = FileSize();
            if (pos + len > fsize)
            {
                std::cerr << "get file len Error" << std::endl;
                ifs.close();
                return false;
            }

            ifs.seekg(pos, std::ios::beg);
            body->resize(len);
            ifs.read(&(*body)[0], len);

            if (ifs.good() == false)
            {
                std::cerr << "ifs read error" << std::endl;
                ifs.close();
                return false;
            }

            ifs.close();
            return true;
        }
        bool GetContent(std::string *body)
        {
            return GetPosLen(body, 0, FileSize());
        }

        bool SetContent(const std::string &body)
        {
            std::ofstream ofs;
            ofs.open(_filename, std::ios::binary);

            if (ofs.is_open() == false)
            {
                std::cerr << "ofs file open error" << std::endl;
                return false;
            }

            ofs.write(&body[0], body.size());
            if (ofs.good() == false)
            {
                std::cerr << "ofs write error" << std::endl;
                ofs.close();
                return false;
            }

            ofs.close();
            return true;
        }

        bool compress(const std::string &packname)
        {
            // 1获取数据
            std::string body;
            if (GetContent(&body) == false)
            {
                std::cerr << "compress getcontent error" << std::endl;
            }

            // 2压缩数据
            std::string packed = bundle::pack(bundle::LZIP, body);

            // 3写入packname文件里
            FileUtil fu(packname);
            if (fu.SetContent(packed) == false)
            {
                std::cerr << "compress write pack error" << std::endl;
                return false;
            }

            return true;
        }

        bool uncompress(const std::string &filename)
        {
            // 将当前压缩包的数据读取出来
            std::string body;
            if (GetContent(&body) == false)
            {
                std::cerr << "uncompress getcontent error" << std::endl;
            }

            // 解压缩
            std::string unpacked = bundle::unpack(body);

            // 写入filename文件里
            FileUtil fu(filename);
            if (fu.SetContent(body) == false)
            {
                std::cerr << "uncompress setcontent error" << std::endl;
                return false;
            }

            return true;
        }

        bool Exists()
        {
            return fs::exists(_filename);
        }

        bool CreateDirectory()
        {
            if (Exists())
            {
                return true;
            }

            return fs::create_directories(_filename);
        }

        bool ScanDirectory(std::vector<std::string> *arry)
        {
            CreateDirectory();
            for (auto &p : fs::directory_iterator(_filename))
            {
                // 如果是目录，不处理
                if (fs::is_directory(p) == true)
                {
                    continue;
                }
                else
                {
                    arry->push_back(fs::path(p).relative_path().string());
                }
            }
        }

    private:
        std::string _filename;
    };

    class JsonUtil
    {
    public:
        static bool Serialize(const Json::Value &root, std::string *str)
        {
            Json::StreamWriterBuilder swb;
            std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());

            std::stringstream ss;
            sw->write(root, &ss);

            *str = ss.str();

            return true;
        }

        static bool UnSerialize(const std::string &str, Json::Value *root)
        {
            std::string error;

            Json::CharReaderBuilder crb;
            std::unique_ptr<Json::CharReader> cr(crb.newCharReader());

            bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), root, &error);
            if(ret == false)
            {
                std::cerr << "parse error" <<std::endl;
                return false;
            }
            return true;
        }
    };
}
